﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
using System.Xml.Linq;
using System.Net;
using System.Xml;
using System.Reflection;
using System.Collections;
using System.Windows.Browser;
using System.IO;

namespace InfoKiosk
{
    public partial class Page : UserControl
    {
        private Int32 position = 0;
        private Int32 maxPosition = 1;
        private Int32 defaultDisplayTime = 2000;
        private String _configurationFile = String.Empty;
        private Timer pageTimer;
        private List<PageRecord> _infoPages;
        private XElement _collectionConfig = null;

        #region public properties
        public Visibility HeaderVisibility
        {
            get { return (Visibility)pageTitle.GetValue(TextBlock.VisibilityProperty); }
            set { pageTitle.SetValue(TextBlock.VisibilityProperty, value); }
        }
        public Double HeaderFontSize
        {
            get { return (Double)pageTitle.GetValue(TextBlock.FontSizeProperty); }
            set { pageTitle.SetValue(TextBlock.FontSizeProperty, value); }
        }
        public FontFamily HeaderFontFamily
        {
            get { return (FontFamily)pageTitle.GetValue(TextBlock.FontFamilyProperty); }
            set { pageTitle.SetValue(TextBlock.FontFamilyProperty, value); }
        }
        public String HeaderForegroundColor
        {
            get { return (String)pageTitle.GetValue(TextBlock.ForegroundProperty); }
            set { pageTitle.SetValue(TextBlock.ForegroundProperty, value); }
        }

        public Visibility ProgressBarVisibility
        {
            get { return (Visibility)ProgressBar.GetValue(Grid.VisibilityProperty); }
            set { ProgressBar.SetValue(Grid.VisibilityProperty, value); }
        }

        public Visibility FooterVisibility
        {
            get { return (Visibility)footer.GetValue(StackPanel.VisibilityProperty); }
            set { footer.SetValue(StackPanel.VisibilityProperty, value); }
        }
        public Double FooterFontSize
        {
            get { return (Double)positionIndicator.GetValue(TextBlock.FontSizeProperty); }
            set { positionIndicator.SetValue(TextBlock.FontSizeProperty, value); }
        }
        public FontFamily FooterFontFamily
        {
            get { return (FontFamily)positionIndicator.GetValue(TextBlock.FontFamilyProperty); }
            set { positionIndicator.SetValue(TextBlock.FontFamilyProperty, value); }
        }
        #endregion

        public Page()
        {
            InitializeComponent();
        }


        /// <summary>
        /// Event fired when the Page is loaded
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            LoadConfigCollection();
        }

        /// <summary>
        /// Method to load the collection of configurations
        /// </summary>
        private void LoadConfigCollection()
        {
            WebClient configCollectionClient;

            try
            {
                configCollectionClient = new WebClient();
                configCollectionClient.OpenReadCompleted += LoadConfigCollectionCompleted;
                configCollectionClient.OpenReadAsync(new Uri("Configurations/ConfigCollection.xml", UriKind.Relative));
            }
            catch (Exception ex)
            {
                //TODO: Implement exception handling
            }

            configCollectionClient = null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LoadConfigCollectionCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            String teamValue = GetElementValueFromQueryString("Team");

            // if the "Team" query element is not found we assume "Default"
            if (teamValue == null)
            {
                teamValue = "Default";
            }

            ConfigureScreen(e.Result, teamValue);

            // get the Info Controls configuration filename
            _configurationFile = GetConfiguration(teamValue);

            if (_configurationFile == null)
            {
                //TODO: Implement user friendly message
                return;
            }

            // Loading the Page data
            LoadInfoControls();

            pageTimer = new Timer(PositionAdvance, null, defaultDisplayTime, defaultDisplayTime);
        }

        /// <summary>
        /// Method to load the Info Control configuration
        /// </summary>
        private void LoadInfoControls()
        {
            WebClient pageControlClient;

            try
            {
                pageControlClient = new WebClient();
                pageControlClient.OpenReadCompleted += LoadInfoControlsCompleted;
                pageControlClient.OpenReadAsync(new Uri(_configurationFile, UriKind.Relative));
            }
            catch (Exception ex)
            {
                //TODO: Implement exception handling
            }

            pageControlClient = null;
        }

        /// <summary>
        /// Event fires when the Info Control Definitions have been read
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LoadInfoControlsCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Result == null)
            {
                //TODO: Add proper messaging to inform the user that the Config file could not be loaded

                return;
            }

            XElement configuration = XElement.Load(
                XmlReader.Create(e.Result));

            _infoPages = InfoControlDefinitions(configuration);

            maxPosition = _infoPages.Count() - 1;

            defaultDisplayTime = Int32.Parse(_infoPages[0].DefaultDisplayTime) * 1000;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="definition"></param>
        private void ConfigureScreen(Stream definition, String configurationName)
        {
            _collectionConfig = XElement.Load(
                     XmlReader.Create(definition));

            var results = from configuration in _collectionConfig.DescendantsAndSelf("Configuration")
                          where (configuration.Attribute("Active") == null ||
                                 configuration.Attribute("Active").Value.Equals("true")) &&
                                 configuration.Attribute("Name").Value.Equals(configurationName)
                          select new XElement(configuration.Element("AppConfig"));

            switch ((Boolean?)results.First().Element("Header").Attribute("Active"))
            {
                case true:
                case null:
                    HeaderVisibility = Visibility.Visible;
                    HeaderFontSize = (Double)results.First().Element("Header").Attribute("FontSize");
                    //HeaderForegroundColor = results.First().Element("Header").Attribute("ForeColor").Value;
                    HeaderFontFamily = new FontFamily((String)results.First().Element("Header").Attribute("FontFamily"));
                    break;
                case false:
                    HeaderVisibility = Visibility.Collapsed;
                    break;
            }

            switch ((Boolean?)results.First().Element("Footer").Attribute("Active"))
            {
                case true:
                case null:
                    FooterVisibility = Visibility.Visible;
                    FooterFontSize = (Double)results.First().Element("Footer").Attribute("FontSize");
                    FooterFontFamily = new FontFamily((String)results.First().Element("Footer").Attribute("FontFamily"));
                    break;
                case false:
                    FooterVisibility = Visibility.Collapsed;
                    break;
            }

            switch ((Boolean?)results.First().Element("ProgressBar").Attribute("Active"))
            {
                case true:
                case null:
                    ProgressBarVisibility = Visibility.Visible;
                    break;
                case false:
                    ProgressBarVisibility = Visibility.Collapsed;
                    break;
            }
        }

        /// <summary>
        /// Loads the Info Control
        /// </summary>
        private void LoadInfoControl()
        {
            WebClient client = new WebClient();
            client.OpenReadCompleted += LoadInfoControlCompleted;
            client.OpenReadAsync(new Uri(_infoPages[position].Dll, UriKind.Relative));
        }

        /// <summary>
        /// Event fired when the info Component DLL is loaded
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LoadInfoControlCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if ((e.Result == null) ||
                (maxPosition < position))
                return;

            AssemblyPart part = new AssemblyPart();
            Assembly assembly = part.Load(e.Result);

            if ((assembly == null) ||
                (_infoPages[position].Title == null))
                return;


            Object control = assembly.CreateInstance(
                _infoPages[position].Type);

            FrameworkElement infoPageControl = control as FrameworkElement;

            if (infoPageControl != null)
            {
                contentLocation.Dispatcher.BeginInvoke(delegate
                {
                    contentLocation.Children.Clear();
                    contentLocation.Children.Add(infoPageControl);
                });


                if ((control as IInfoPage).ComponentParameters == null)
                {
                    (control as IInfoPage).ComponentParameters = new List<IComponentParameter>();
                }

                foreach (ComponentParameter param in _infoPages[position].ComponentParameters)
                {

                    (control as IInfoPage).ComponentParameters.Add(new ComponentParameter()
                    {
                        Key = param.Key,
                        Value = param.Value
                    });
                }
                (control as IInfoPage).Start();
            }

            position++;
        }

        #region Helper Methods
        /// <summary>
        /// Advances to the next page
        /// </summary>
        /// <param name="state"></param>
        public void PositionAdvance(Object state)
        {
            if (_infoPages == null)
                return;

            if (position > maxPosition)
                position = 0;

            pageTitle.Dispatcher.BeginInvoke(delegate
            {
                pageTitle.Text = _infoPages[position].Title;
            });

            if (_infoPages[position].DisplayTime != null)
            {
                Int32 displayTime = Int32.Parse(_infoPages[position].DisplayTime) * 1000;

                pageTimer.Change(displayTime, defaultDisplayTime);
            }
            else
            {
                pageTimer.Change(defaultDisplayTime, defaultDisplayTime);
            }

            if ((_infoPages[position].Dll != null) &&
                (_infoPages[position].Type != null))
            {
                // Loading the Info Control
                this.Dispatcher.BeginInvoke(LoadInfoControl);
            }
            else
            {
                contentLocation.Dispatcher.BeginInvoke(delegate
                {
                    contentLocation.Children.Clear();
                });
            }

            positionMarker_Begin.Dispatcher.BeginInvoke(delegate
            {
                positionMarker_Begin.Width = new GridLength(position + 1, GridUnitType.Star);
            });

            positionMarker_End.Dispatcher.BeginInvoke(delegate
            {
                positionMarker_End.Width = new GridLength(maxPosition - position, GridUnitType.Star);
            });

            positionIndicator.Dispatcher.BeginInvoke(delegate
            {
                if (_infoPages != null)
                {
                    positionIndicator.Text = String.Format("Page {0} of {1}", position + 1, maxPosition + 1);
                }
            });

            // Loading the Page data
            this.Dispatcher.BeginInvoke(LoadInfoControls);
        }

        /// <summary>
        /// Retrieves an element from the Page QueryString
        /// </summary>
        /// <param name="key">The name of the element to retrieve from the QueryString</param>
        /// <returns>A <see cref="String"/> with the value of the requested querystring element</returns>
        private String GetElementValueFromQueryString(String key)
        {
            IDictionary<String, String> queryStringItems = HtmlPage.Document.QueryString;

            foreach (KeyValuePair<String, String> queryStringItem in queryStringItems)
            {
                if (queryStringItem.Key.Equals(key))
                {
                    return queryStringItem.Value;
                }
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="configName"></param>
        /// <param name="configStream"></param>
        /// <returns></returns>
        private String GetConfiguration(String configName)
        {
            try
            {
                var results = from configuration in _collectionConfig.DescendantsAndSelf("Configuration")
                              where (configuration.Attribute("Active") == null ||
                                     configuration.Attribute("Active").Value.Equals("true")) &&
                                     configuration.Attribute("Name").Value.Equals(configName)
                              select new { File = (String)configuration.Attribute("File") };

                return (String)results.First().File;
            }
            catch (Exception ex)
            {
                //TODO: Implement exception handling
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="infoControlDefinitions"></param>
        /// <returns></returns>
        private List<PageRecord> InfoControlDefinitions(XElement infoControlDefinitions)
        {
            try
            {
                var result = from pages in infoControlDefinitions.DescendantsAndSelf("Page")
                             where pages.Attribute("Active") == null ||
                                   pages.Attribute("Active").Value.Equals("true")
                             select new PageRecord
                             {
                                 Title = (String)pages.Attribute("Title"),
                                 DisplayTime = (String)pages.Attribute("DisplayTime"),
                                 Active = (Boolean?)pages.Attribute("Active"),
                                 DefaultDisplayTime = (String)pages.Ancestors("Pages").First().Attribute("DefaultDisplayTime"),
                                 Dll = (String)pages.Descendants("Component").First().Attribute("Dll"),
                                 Type = (String)pages.Descendants("Component").First().Attribute("Type"),
                                 ComponentParameters = from parameters in pages.Element("ComponentParameters").Elements("Parameter")
                                                       select new ComponentParameter
                                                       {
                                                           Key = (String)parameters.Attribute("Key"),
                                                           Value = (String)parameters.Attribute("Value")
                                                       }
                             };

                return result.ToList<PageRecord>();
            }
            catch (Exception ex)
            {
                //TODO: Implement Exception handling
            }

            return null;
        }

        #endregion
    }
}
